package com.xiaoxin.experience.service.ssh;

import com.xiaoxin.experience.service.ssh.model.RemoteFileInfo;
import com.xiaoxin.experience.service.ssh.model.SSHSessionInfo;
import com.xiaoxin.experience.util.CollectionUtil;
import com.xiaoxin.experience.util.DateUtil;
import com.xiaoxin.experience.util.FileUtil;
import com.xiaoxin.experience.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.connection.channel.direct.Session;
import net.schmizz.sshj.sftp.FileAttributes;
import net.schmizz.sshj.sftp.RemoteResourceInfo;
import net.schmizz.sshj.sftp.SFTPClient;
import net.schmizz.sshj.transport.verification.PromiscuousVerifier;
import net.schmizz.sshj.xfer.FileSystemFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author xiaoxin
 */
@Slf4j
public class SSHJClient
{
    private final SSHClient ssh;

    private SFTPClient sftp;

    public SSHJClient(SSHSessionInfo sshSessionInfo)
            throws IOException
    {
        this(sshSessionInfo.getIp(),sshSessionInfo.getPort(),sshSessionInfo.getUsername(),sshSessionInfo.getPassword());
    }

    public SSHJClient(String ip, int port, String username, String password)
            throws IOException
    {
        // 创建 SSH 客户端
        ssh = new SSHClient();
        // 加载已知hosts,建议不加载
//        ssh.loadKnownHosts();
        // 跳过密钥验证
        ssh.addHostKeyVerifier(new PromiscuousVerifier());
        // 传入主机名或IP地址建立连接
        ssh.connect(ip, port);
        // 通过用户名和密码进行验证
        ssh.authPassword(username, password);
    }

    public void putFile(String remoteDir,String localFilePath)
            throws IOException
    {
        putFiles(Collections.singletonList(new String[]{remoteDir,localFilePath}));
    }

    public void putFiles(List<String[]> remoteDirAndLocalFilePaths)
            throws IOException
    {
        try (SFTPClient sftp = ssh.newSFTPClient())
        {
            for (String[] remoteDirAndLocalFilePath : remoteDirAndLocalFilePaths)
            {
                String remoteDir = remoteDirAndLocalFilePath[0];
                String localFilePath = remoteDirAndLocalFilePath[1];
                sftp.put(new FileSystemFile(localFilePath), remoteDir);
            }
        }
    }

    public void getFile(String remoteFilePath,String localDir)
            throws IOException
    {
        getFiles(Collections.singletonList(remoteFilePath),localDir);
    }

    public void getFiles(List<String> remoteFileNames, String localDir)
            throws IOException
    {
        SFTPClient sftp = getSftp();
        for (String remoteFileName : remoteFileNames)
        {
            sftp.get(remoteFileName, localDir);
        }
    }

    public List<RemoteFileInfo> getFileNames(String remoteDir)
            throws IOException
    {
        SFTPClient sftp = getSftp();

        List<RemoteResourceInfo> ls = sftp.ls(remoteDir);
        if (CollectionUtil.isEmpty(ls))
        {
            return Collections.emptyList();
        }
        return ls.stream().map(this::buildRemoteFileInfo).collect(Collectors.toList());

    }

    private RemoteFileInfo buildRemoteFileInfo(RemoteResourceInfo remoteResourceInfo)
    {
        RemoteFileInfo remoteFileInfo = new RemoteFileInfo();
        String name = remoteResourceInfo.getName();
        remoteFileInfo.setId(name);
        remoteFileInfo.setName(name);
        remoteFileInfo.setLabel(buildLabel(remoteResourceInfo));
        remoteFileInfo.setType(remoteResourceInfo.isDirectory() ? 1 : 0);
        return remoteFileInfo;
    }

    private String buildLabel(RemoteResourceInfo resourceInfo)
    {
        String name = getFixString(resourceInfo.getName(),40);
        FileAttributes attributes = resourceInfo.getAttributes();
        String createTime = DateUtil.formatTimeStd(attributes.getMtime() * 1000);
        String updateTime = DateUtil.formatTimeStd(attributes.getAtime() * 1000);
        String fileSize = getFixString(FileUtil.getHumanFileSize(attributes.getSize()),8);
        String format = "%s%s %s   %s";
        return String.format(format,name,fileSize,createTime,updateTime).replace(" ","\u00a0");
    }

    private String getFixString(String str,int length)
    {
        if (str.length() < length)
        {
            return str + StringUtil.repeat(' ',length - str.length());
        }
        return str;
    }

    public void shellCmd(String ... commands)
            throws IOException
    {
        // 开启会话
        try(Session session = ssh.startSession())
        {
            session.allocateDefaultPTY();
            try(Session.Shell shell = session.startShell())
            {
                InputStream inputStream = shell.getInputStream();
                OutputStream os = shell.getOutputStream();
                PrintWriter printWriter = new PrintWriter(os);
                for (String command : commands)
                {
                    printWriter.println(command);
                }
                // 退出
                printWriter.println("exit");
                printWriter.flush();
                // 执行命令

                // 获取命令的执行结果
                String response = IOUtils.readFully(inputStream).toString();
                // 打印命令的执行结果
                log.debug(response);
                shell.join(10, TimeUnit.SECONDS);
            }
        }
    }

    public SFTPClient getSftp()
            throws IOException
    {
        if (sftp == null)
        {
            sftp = ssh.newSFTPClient();
        }
        return sftp;
    }

    public void release() throws IOException
    {
        if (Objects.nonNull(sftp))
        {
            sftp.close();
        }
        ssh.disconnect();
        ssh.close();
    }

}
